//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	DictionaryExts.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Threading;

namespace Smart
{
	[DebuggerStepThrough]
	public static class DictionaryExts
	{
		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static TValueType SafeGet<TKeyType, TValueType>(this IDictionary<TKeyType, TValueType> values, TKeyType key)
		{
			TValueType value;

			values.TryGetValue(key, out value);

			return value;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static TValueType SafeGet<TKeyType, TValueType>(this IDictionary<TKeyType, TValueType> values, TKeyType key, TValueType defaultValue)
		{
			TValueType value;

			if (!values.TryGetValue(key, out value))
				value = defaultValue;

			return value;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>, if <paramref name="key"/> do not exists uses <paramref name="loader"/> to add it to dictionary
		/// </summary>
		public static TValueType SafeGetLoad<TKeyType, TValueType>(this IDictionary<TKeyType, TValueType> values, TKeyType key, Func<TKeyType, TValueType> loader)
		{
			TValueType value;

			if (!values.TryGetValue(key, out value))
				values[key] = value = loader(key);

			return value;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>, if <paramref name="key"/> do not exists uses <paramref name="loader"/> to add it to dictionary
		/// </summary>
		public static TValueType SafeGetLoad<TKeyType, TValueType>(this IDictionary<TKeyType, TValueType> values, TKeyType key, ReaderWriterLockSlim syncObj, Func<TKeyType, TValueType> loader)
		{
			TValueType value;

			using (new UpgradeableReadLock(syncObj))
				if (!values.TryGetValue(key, out value))
					using (new WriteLock(syncObj))
						if (!values.TryGetValue(key, out value))
							values[key] = value = loader(key);

			return value;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>, if <paramref name="key"/> do not exists uses <paramref name="loader"/> to add it to dictionary
		/// </summary>
		public static TValueType SafeGetLoad<TKeyType, TValueType>(this Hashtable values, TKeyType key, Func<TKeyType, TValueType> loader)
		{
			var value = values[key];

			if (value == null)
				values[key] = value = loader(key);

			return (TValueType)value;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static TValueType SafeGet<TKeyType, TValueType>(this Hashtable values, TKeyType key, TValueType defaultValue)
		{
			var value = values[key];

			return (TValueType)(value ?? defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static TValueType SafeGet<TKeyType, TValueType>(this Hashtable values, TKeyType key)
		{
			return (TValueType)values[key];
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static string SafeGetStr<TKeyType>(this Hashtable values, TKeyType key, string defaultValue)
		{
			return values[key].SToStr(defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static DateTime SafeGetDT<TKeyType>(this Hashtable values, TKeyType key, DateTime defaultValue)
		{
			return values[key].SToDT(defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static double SafeGetDbl<TKeyType>(this Hashtable values, TKeyType key, double defaultValue)
		{
			return values[key].SToDbl(defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static int SafeGetInt<TKeyType>(this Hashtable values, TKeyType key, int defaultValue)
		{
			return values[key].SToInt(defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static bool SafeGetBool<TKeyType>(this Hashtable values, TKeyType key, bool defaultValue)
		{
			return values[key].SToBool(defaultValue);
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static string SafeGetStr<TKeyType>(this IDictionary<TKeyType, object> values, TKeyType key, string defaultValue)
		{
			object value;

			if (values.TryGetValue(key, out value) && value != null)
				return value.SToStr(defaultValue);

			return defaultValue;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static DateTime SafeGetDT<TKeyType>(this IDictionary<TKeyType, object> values, TKeyType key, DateTime defaultValue)
		{
			object value;

			if (values.TryGetValue(key, out value) && value != null)
				return value.SToDT(defaultValue);

			return defaultValue;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static double SafeGetDbl<TKeyType>(this IDictionary<TKeyType, object> values, TKeyType key, double defaultValue)
		{
			object value;

			if (values.TryGetValue(key, out value) && value != null)
				return value.SToDbl(defaultValue);

			return defaultValue;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static int SafeGetInt<TKeyType>(this IDictionary<TKeyType, object> values, TKeyType key, int defaultValue)
		{
			object value;

			if (values.TryGetValue(key, out value) && value != null)
				return value.SToInt(defaultValue);

			return defaultValue;
		}

		/// <summary>
		/// Safely return item value by <paramref name="key"/>
		/// </summary>
		public static bool SafeGetBool<TKeyType>(this IDictionary<TKeyType, object> values, TKeyType key, bool defaultValue)
		{
			object value;

			if (values.TryGetValue(key, out value) && value != null)
				return value.SToBool(defaultValue);

			return defaultValue;
		}

		/// <summary>
		/// Translate <paramref name="key"/> to value in <paramref name="values"/> dictionary, if no value exists or value is null returns <paramref name="key"/>
		/// </summary>
		public static string Translate(this StringDictionary values, string key)
		{
			return values[key] ?? key;
		}

		/// <summary>
		/// Translate <paramref name="key"/> to value in <paramref name="values"/> dictionary, if no value exists or value is null returns <paramref name="key"/>
		/// </summary>
		public static TType Translate<TType>(this IDictionary<TType, TType> values, TType key)
			where TType : class
		{
			return values.SafeGet(key, key) ?? key;
		}

		/// <summary>
		/// Return dictionary values hash code
		/// </summary>
		public static int GetValuesHashCode<TKeyType, TValueType>(Dictionary<TKeyType, TValueType> values) where TValueType : class
		{
			var hash = 0;

			if (values != null)
			{
				var comparer = values.Comparer;

				foreach (var kvp in values)
					hash = hash ^ comparer.GetHashCode(kvp.Key) ^ (kvp.Value == null ? 0 : kvp.Value.GetHashCode());
			}

			return hash;
		}

		/// <summary>
		/// Return dictionary values hash code formatted as hex string
		/// </summary>
		public static string GetValuesHashCodeX<TKeyType, TValueType>(this Dictionary<TKeyType, TValueType> values)
			where TValueType : class
		{
			var hash = GetValuesHashCode(values);

			return hash.ToStringX();
		}

		public static object SafeGet(this Hashtable values, object key)
		{
			return
				SafeGet(values, key, null);
		}

		public static object SafeGet(this Hashtable values, object key, object defaultValue)
		{
			return
				values.ContainsKey(key)
					? values[key]
					: defaultValue;
		}
	}
}